Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jwks parsing (abandoned) #112

Closed
wants to merge 25 commits into from
Closed

Conversation

dtiukalov
Copy link

@dtiukalov dtiukalov commented Dec 2, 2020

add jwks implementation to simple access to x5c to verify jwt:

auto decoded_jwt = jwt::decode(token);
auto jwks = jwt::parse_jwks(jwks_json_str);
if (jwks.has_x5c_id()) {
   auto issuer = decoded_jwt.get_issuer();
   auto x5c = jwks.get_x5c();
   if (jwks.get_key_id() == decoded_jwt.get_key_id()) {
      if (!x5c.empty() && !issuer.empty()) {
         auto verifier = jwt::verify()
            .allow_algorithm(jwt::algorithm::rs256(jwt::helper::convert_base64_der_to_pem(x5c), "", "", ""))
            .with_issuer(issuer)
            .leeway(60U); 
         std::error_code err;
         verifier.verify(decoded_jwt, err);
         jwt::error::throw_if_error(err);
     }
}
   else {
      std::cerr << "Key ids are not matched" << std::endl;
   }
}

add jwks implementation to simple access to x5c to verify jwt
@prince-chrismc prince-chrismc self-assigned this Dec 2, 2020
@prince-chrismc
Copy link
Collaborator

Wow! 🏆 super exciting to see this added, thank you so much for your contribution ❤️

This would be a start towards #94 which I think a lot of people would benefit from!

There's a few key details missing in order for this to be merged

  • Test code
    • Something similar to this test
    • Feel free to add a new file
  • No base64 (required by the helper you are using)
    • You'll need to add a overload like this
      template<typename Decode>
      std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode, std::error_code& ec) {
  • Example
    • I think the code you wrote above is excellent, just stick it in the examples!

I very happy to see this PR get merged, let me know if you need more help! 🚀

auto verifier = jwt::verify()
.allow_algorithm(jwt::algorithm::rs256(jwt::helper::convert_base64_der_to_pem(x5c), "", "", ""))
.with_issuer(issuer)
.leeway(10000000000UL); // value in seconds, add some to compensate timeout
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, a simple token with no expiration is possible?

example/jwks-verify.cpp Outdated Show resolved Hide resolved
include/jwt-cpp/jwt.h Outdated Show resolved Hide resolved

public:
JWT_CLAIM_EXPLICIT jwks(const typename json_traits::string_type& jwks_token) {
auto parse_claims = [](const typename json_traits::string_type& str) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this block seems to be copy pasted from the original

📓 for myself to look into a small refactor!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, and it costs an extra move, good point to refactor.

include/jwt-cpp/jwt.h Outdated Show resolved Hide resolved
include/jwt-cpp/jwt.h Outdated Show resolved Hide resolved
include/jwt-cpp/jwt.h Outdated Show resolved Hide resolved
};

template<typename json_traits>
class jwks_keys {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this "parser" could be a static method that returns a vector, this was the implementation is not limited on KIDs being used in all the keys

Thoughts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_vector() is added

Copy link
Collaborator

@prince-chrismc prince-chrismc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look really good! 👑

There's a few more details about x5c handling I would appreciate your thoughts on.

Lastly, I need to get my note book and review them, I may come back with a suggestion to make the jwks more generic... there's a lot of variety with the content and I want to make sure. Hopefully you can understand!

Keep up the awesome work 💪


/**
* Get x5c claim
* \return x5c as string
* \throw std::runtime_error If claim was not present
* \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
*/
typename json_traits::string_type get_x5c() const { return json_traits::as_string(get_jwks_claim("x5c").as_array().front()); } //do not use serialize() instead
typename json_traits::string_type get_x5c() const { return json_traits::as_string(get_jwk_claim("x5c").as_array().front()); } //do not use serialize() instead
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the consumer will/may need the array in larger systems as per the rfc 4.7

The PKIX certificate containing the key value MUST be the first
certificate. This MAY be followed by additional certificates, with
each subsequent certificate being the one used to certify the
previous one.

What do you think about having two functions?

typename json_traits::array_type get_x5c() const;
typename json_traits::string_type get_x5c_key_value() const;

Secondly, front might be a little dangerous! Calling front on an empty container is undefined

please throw if empty 💥

Comment on lines 2809 to 2812
typename json_traits::string_type error_msg = "jwk with key id \"";
error_msg += key_id;
error_msg += "\" not found";
throw std::runtime_error(error_msg.c_str());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the caller knows the kid they passed, no need to repeat it

Suggested change
typename json_traits::string_type error_msg = "jwk with key id \"";
error_msg += key_id;
error_msg += "\" not found";
throw std::runtime_error(error_msg.c_str());
throw std::runtime_error("jwk with matching key id not found");


private:

std::unordered_map<typename json_traits::string_type, jwks_t> jwks_keys_claims;
std::unordered_map<typename json_traits::string_type, jwk_t> jwks_claims;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only because i've implemented this and used a few different auth servers... I am really unsure about this being a map...

What if there are two keys neither have KIDs? One would be lost, most likely the new key that is starting to issue tokens.

Comment on lines +2784 to +2794
for (const auto& k : json_traits::as_object(val)) {
size_t id = 0;
for (const typename json_traits::value_type& item : json_traits::as_array(k.second)) {
jwk_t jwk_entry(item);
if (jwk_entry.has_key_id())
res.emplace(jwk_entry.get_key_id(), std::move(jwk_entry));
else
res.emplace(std::to_string(id), std::move(jwk_entry));
++id;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, so I would really like if we could leave the KID outside of the JWKS logic because it's important to support workflows that do not required them.

I will come back with some changes against your branch, I think the biggest limitation is the parsing or "map of claims"

I also see we don't check "keys" explicitly is the JWKS which would open use to a vulnerability

@prince-chrismc
Copy link
Collaborator

@dtiukalov please consider dtiukalov#1

@Thalhammer Thalhammer changed the title Update jwt.h Add jwks parsing Feb 2, 2021
@prince-chrismc prince-chrismc removed their assignment Mar 24, 2021
@rasmuspeders1
Copy link

This looks great! Basically this is exactly what I was looking for. A single nice c++ library to handle jwt auth including jwk handling.
Looking forward to trying it out when it is ready.

@prince-chrismc
Copy link
Collaborator

Looking forward to trying it out when it is ready.

Sadly it seems OP has stopped working on this, it would be greatly appreciated if you could pick up and work on this!

Lots of work has been done just needs a little push to get it over

@rasmuspeders1
Copy link

Looking forward to trying it out when it is ready.

Sadly it seems OP has stopped working on this, it would be greatly appreciated if you could pick up and work on this!

Lots of work has been done just needs a little push to get it over

That is too bad.
I am not sure I will have the time in the near future nor possess the necessary skill. I will keep it in mind if I find some time.

@ghost
Copy link

ghost commented Jun 28, 2021

@prince-chrismc what's left in this PR? maybe i can take a shot at this

@prince-chrismc
Copy link
Collaborator

I attempted to help the original author and made a PR against his branch.... https://github.com/prince-chrismc/jwt-cpp/tree/add-jwks is the latest code

Sadly it's been a while and I do not recall the details...
Scrolling over the conversation, I had some concerns the implementation was limiting some JOSE workflows... I see some test code but more is always better

If you have time to push this feature I'd love to help!

@Thalhammer
Copy link
Owner

It will probably also need a pretty significant update to the current version.

@ghost
Copy link

ghost commented Jun 29, 2021

Does it make sense to start a new PR with OP's changes + @prince-chrismc 's suggestions to it, targetting the latest version? I think looking at the latest diff might help us see what's left

@ghost ghost mentioned this pull request Jun 29, 2021
@Thalhammer Thalhammer changed the title Add jwks parsing Add jwks parsing (abandoned) Jun 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants